﻿using DatabaseGroup.SSIS.SourceAdapters;
using DatabaseGroup.SSIS.SourceAdapters.Model;
using DatabaseGroup.SSIS.SourceAdapters.Presenter;
using DatabaseGroup.SSIS.SourceAdapters.View;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime;
using RunWrapper = Microsoft.SqlServer.Dts.Runtime.Wrapper;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.IO;

namespace DatabaseGroup.IntegrationTests.SSIS.SourceAdapters
{
    public class DFSIntegrationTestPackage
    {
        private readonly Application _application = new Application();
        private readonly Package _package = new Package();
        private TestContext _testContext;
        private IDTSComponentMetaData90 _dfs;
        private CManagedComponentWrapper _dfsDesigner;
        private ComponentModel _model;
        private AdvanceTabPresenter _advPresenter;
        private AuditTabPresenter _audPresenter;
        private ColumnTabPresenter _colPresenter;
        private ErrorTabPresenter _errPresenter;
        private GeneralTabPresenter _genPresenter;
        private MockView _view;
        private string _stdDestFile = null;
        private string _logDestFile = null;
        private string _errDestFile = null;

        public DFSIntegrationTestPackage(string inputFile, TestContext testContext)
        {
            this.InputFile = inputFile;
            _testContext = testContext;
            _dfs = AutomationHelper.SetupBaseDFSComponent(_package);
            _dfsDesigner = _dfs.Instantiate();
            _model = new ComponentModel(_dfs, new DtsConnectionServiceImpl(), _package.Connections);
            
            _view = new MockView();

            _genPresenter = new GeneralTabPresenter(_view, _model);
            _colPresenter = new ColumnTabPresenter(_view, _model, null);
            _advPresenter = new AdvanceTabPresenter(_view, _model);
            _errPresenter = new ErrorTabPresenter(_view, _model);
            _audPresenter = new AuditTabPresenter(_view, _model);

            this.GeneralSettings = _view.GetTabSettings<GeneralTabSettings>(EditorViewTab.General);
            this.ColumnSettings = _view.GetTabSettings<ColumnTabSettings>(EditorViewTab.Columns);
            this.AdvanceSettings = _view.GetTabSettings<AdvanceTabSettings>(EditorViewTab.Advance);
            this.ErrorSettings = _view.GetTabSettings<ErrorTabSettings>(EditorViewTab.Error);
            this.AuditingSettings = _view.GetTabSettings<AuditTabSettings>(EditorViewTab.Auditing);

            this.GeneralSettings.FileConnection = AutomationHelper.AddFileManager(_package.Connections, "FILE", inputFile);
            this.ErrorSettings.ComponentErrorHandling = PresenterUtils.ConvertDispositionToString(DTSRowDisposition.RD_FailComponent);
            this.ErrorSettings.ComponentTruncationHandling = PresenterUtils.ConvertDispositionToString(DTSRowDisposition.RD_RedirectRow);
        }

        public string InputFile { get; set; }
        public GeneralTabSettings GeneralSettings { get; set; }
        public ColumnTabSettings ColumnSettings { get; set; }
        public AdvanceTabSettings AdvanceSettings { get; set; }
        public ErrorTabSettings ErrorSettings { get; set; }
        public AuditTabSettings AuditingSettings { get; set; }

        public void SaveToPackage()
        {
            _view.SaveSettings();

            if (this.AuditingSettings.IncludeLogOutput)
                _logDestFile = AddDestination("LogDest", TestStrings.DFSLogOutName, TestStrings.FFLogName);

            _errDestFile = AddDestination("ErrorDest", TestStrings.DFSErrOutName, TestStrings.FFErrName);
            _stdDestFile = AddDestination("StandardDest", TestStrings.DFSStdOutName, TestStrings.FFStdName);
        }

        public void SavePackageToFile(string file)
        {
            SaveToPackage();
            AutomationHelper.SavePackage(_package, file);
        }

        public void Execute()
        {
            SaveToPackage();
            _package.Execute();
        }

        public bool IsStandardOutputFileSameAs(string file)
        {
            return IsStandardOutputFileSameAs(file, 0, null);
        }

        public bool IsStandardOutputFileSameAs(string file, int skipInitialLines, string headerDelimiter)
        {
            return AreFilesSame(file, _stdDestFile, skipInitialLines, headerDelimiter);
        }

        public bool IsLogOutputFileSameAs(string file)
        {
            return IsLogOutputFileSameAs(file, 0, null);
        }

        public bool IsLogOutputFileSameAs(string file, int skipInitialLines, string headerDelimiter)
        {
            return AreFilesSame(file, _logDestFile, skipInitialLines, headerDelimiter);
        }

        public bool IsErrorOutputFileSameAs(string file)
        {
            return IsErrorOutputFileSameAs(file, 0, null);
        }

        public bool IsErrorOutputFileSameAs(string file, int skipInitialLines, string headerDelimiter)
        {
            return AreFilesSame(file, _errDestFile, skipInitialLines, headerDelimiter);
        }

        public ColumnProperty CreateCol(string name, int precision, int scale, RunWrapper.DataType datatype, int length, int codepage)
        {
            ColumnProperty col = new ColumnProperty(name, precision, scale, datatype, length, codepage);
            col.ErrorHandling = PresenterUtils.ConvertDispositionToString(DTSRowDisposition.RD_RedirectRow);
            col.TruncationHandling = PresenterUtils.ConvertDispositionToString(DTSRowDisposition.RD_IgnoreFailure);

            return col;
        }

        private bool AreFilesSame(string file1, string file2, int skipInitialLines, string headerDelimiter)
        {
            string whole1 = File.ReadAllText(file1);
            string whole2 = File.ReadAllText(file2);

            if (whole1 == whole2 || (string.IsNullOrEmpty(whole1) && string.IsNullOrEmpty(whole2)))
                return true;

            if (skipInitialLines > 0)
            {
                string[] lines = whole1.Split(new string[] { headerDelimiter }, System.StringSplitOptions.None);
                whole1 = string.Join(headerDelimiter, lines, skipInitialLines, lines.Length - skipInitialLines);
            }

            if (!whole1.EndsWith("\r\n") && whole2.EndsWith("\r\n"))
            {
                whole2 = whole2.Substring(0, whole2.Length - 2);
            }

            return whole1 == whole2;
        }

        private string AddDestination(string fileName, string dfsOutputName, string ffName)
        {
            string outputFile = Path.Combine(_testContext.TestDir, _testContext.TestName + "_" + fileName + ".txt");

            FlatFileConfig ffConfig = CreateFlatFileConfig();

            if (dfsOutputName == TestStrings.DFSErrOutName || dfsOutputName == TestStrings.DFSLogOutName)
            {
                ffConfig.CodePage = 1252;
                ffConfig.ColumnNamesInFirstDataRow = false;

                // need to remove this column otherwise the result files will never match 
                ffConfig.IgnoreColumns.Add("Occurred");
            }

            string outputFileManager = AutomationHelper.AddFlatFileConnection(_package, outputFile, TestStrings.DFSName, dfsOutputName, ffConfig);
            AutomationHelper.AddOutputDestination(_package, ffName, _application.PipelineComponentInfos["Flat File Destination"].CreationName, outputFileManager);
            AutomationHelper.LinkComponents(_package, TestStrings.DFSName, dfsOutputName, ffName, TestStrings.FFInputName, ffConfig);
            return outputFile;
        }

        private FlatFileConfig CreateFlatFileConfig()
        {
            string textQualifier = DelimiterStringConverter.ToValueDelim(this.GeneralSettings.TextQualifier);
            bool isTextQualified = !string.IsNullOrEmpty(textQualifier);

            return new FlatFileConfig()
                       {
                           CodePage = PresenterUtils.ConvertStringToCodepage(this.GeneralSettings.CodePage),
                           ColumnDelimiter = DelimiterStringConverter.ToValueDelim(this.ColumnSettings.ColumnDelimiter),
                           ColumnNamesInFirstDataRow = this.GeneralSettings.ColumnNamesInFirstDataRow,
                           IsTextQualified = isTextQualified,
                           RowDelimiter = DelimiterStringConverter.ToValueDelim(this.ColumnSettings.RowDelimiter),
                           TextQualifier = textQualifier,
                           Unicode = this.GeneralSettings.IsUnicode
                       };
        }
    }
}
